package statisticabusiness.engine;

import java.util.ArrayList;
import java.util.HashMap;

/**
 * Implementazione del metodo di McQuitty
 * @author Gagliano Turi, Giuffrida Alberto
 */
public class McQuitty {
 int cont;
    ArrayList<double[][]> matriciSequenziali;
    double[][] d1,d2;
    double[] fusione;
    int[] posizione;
    ArrayList<int[]> dendr;
    HashMap<Integer,Double> vd;
    
    /**
     * Metodo costruttore<br/>
     * MatriciSequenziali  deve contiene all'inizio solo una matrice(creata col 
     * metoo distanza di Minkowski) che sarà poi 
     * utilizzata nel metodo; cont &egrave; l'indice per scorrere matriciSequenziali
     * ( di solito inizialmente pari a zero).<br/>
     * @param cont      contatore intero per scorrere matriciSequenziali
     * @param matriciSequenziali    lista delle matrici che si ricavano ad ogni iterazione del metodo
     * @param dendr     lista delle coordinate che identificano le unioni nella matrice(o meglio dei gruppi)
     * @param vd        lista contenente il numero di gruppi ottenuto e la distanza al momento dell'unione
     */
    public McQuitty(int cont, ArrayList<double[][]> matriciSequenziali, ArrayList<int[]> dendr, HashMap<Integer,Double> vd){
  
        this.cont = cont;
        this.matriciSequenziali= matriciSequenziali;
        this.dendr= dendr;
        this.vd=vd;
    }
    
    /**
     * Avvia il calcolo del metodo di McQuitty<br/>
     * Essendo molto simile a Legame Completo per delucidazioni sul codice interno
     * guardare la classe LegameCompleto che risulta essere tutta commentata in dettaglio
     */
    public void mcQuitty(){
        
        
        
        while(!(matriciSequenziali.get(cont).length==2)){

            posizione= new int[2];
            posizione = this.ricercaMinore();
            //creo la matrice che mettero poi nella lista matriciSequenziali
            d1 = new double[matriciSequenziali.get(cont).length-1][matriciSequenziali.get(cont).length-1];
            d2 = matriciSequenziali.get(cont); //carico la matrice precedente
            fusione=new double[matriciSequenziali.get(cont).length]; 

            dendr.add(posizione);
            
            //unico ciclo diverso da Legame Completo qui calcola la distanza secondo McQuitty
            for(int rig=0; rig<d2.length; rig++){
                fusione[rig]= ((d2[posizione[0]][rig]+d2[posizione[1]][rig])/2.0);
            }

            for(int i=0; i<d2.length; i++){
                //nel for successivo faccio partire j da i+1 cosi riempio solo il triangolo superiore

                if(i==posizione[0] || i==posizione[1]) continue;

                for(int j=i+1; j<d2[i].length; j++){   

                    if(j==posizione[0] || j==posizione[1]) continue;
                    
                    if(i<posizione[0] && i<posizione[1]){
                        if(j<posizione[0] && j<posizione[1]) d1[i][j]= d2[i][j];
                        if(j>posizione[0] && j<posizione[1]) d1[i][j-1]= d2[i][j];
                        if(j>posizione[0] && j>posizione[1]) d1[i][j-2]= d2[i][j];
                    }
                    
                    if(i>posizione[0] && i<posizione[1]){
                        if(j<posizione[0] && j<posizione[1]) d1[i-1][j]= d2[i][j];
                        if(j>posizione[0] && j<posizione[1]) d1[i-1][j-1]= d2[i][j];
                        if(j>posizione[0] && j>posizione[1]) d1[i-1][j-2]= d2[i][j];
                    }
                    if(i>posizione[0] && i>posizione[1]){
                        if(j<posizione[0] && j<posizione[1]) d1[i-2][j]= d2[i][j];
                        if(j>posizione[0] && j<posizione[1]) d1[i-2][j-1]= d2[i][j];
                        if(j>posizione[0] && j>posizione[1]) d1[i-2][j-2]= d2[i][j];
                    }
                }
            }
            for(int i=0; i<fusione.length; i++){          
                if(i<posizione[0] && i<posizione[1]){
                        d1[i][d1.length-1]=fusione[i];
                }                
                if(i>posizione[0] && i<posizione[1]){
                        d1[i-1][d1.length-1]=fusione[i];
                }                
                if(i>posizione[0] && i>posizione[1]){
                        d1[i-2][d1.length-1]=fusione[i];
                }  
                if(i==posizione[0] && i==posizione[1]) {
                    continue;
                }
            }
            cont++;
            this.completaMatrice(d1);
            matriciSequenziali.add(d1);
            stampa(d1);
        }
    }
    
    /**
     * Trova l'elemento della matrice corrente(matriciSequenziali.get(cont)) 
     * col valore minore e ne restituice le coordinate
     * @return  le coordinate dell'elemento minore
     */
    public int[] ricercaMinore(){
        
        posizione[0]= 0;  //parto dalla prima posizione utile la 0,0 e' nella diagonale quindi e' nulla
        posizione[1]= 1;
        double[][] distanze = this.matriciSequenziali.get(cont);
        double minimo= distanze[0][1];
        
        for(int i=0; i<matriciSequenziali.get(cont).length; i++){
            
            for(int j=i+1; j<matriciSequenziali.get(cont)[i].length; j++){  
                
                if(matriciSequenziali.get(cont)[i][j]<minimo){
                    minimo=matriciSequenziali.get(cont)[i][j];
                    posizione[0]=i;
                    posizione[1]=j;
                }
            }
         }
        vd.put( (matriciSequenziali.get(cont).length-1), minimo);
        return posizione;
    }
    
    /**
     * A partire da una triangolare superiore restituisce una triangolare competa
     * con la diagonale nulla
     * @param d  matrice da completare
     */
    public void completaMatrice(double[][] d){
        
        for(int i=1; i<d.length; i++){
            
            for(int j=0; j<i; j++){  
                    d[i][j]=d[j][i];  
            }      
        }
    }
    
    
    /**
     * Stampa il contenuto di una matrice nella console. <br/>
     * Utile in fase di debug
     * @param d
     */
    public void stampa(double[][] d){
        for(int i=0; i<d.length; i++){
            //nel for successivo faccio partire j da i+1 cosi riempio solo il triangolo superiore
            for(int j=0; j<d.length; j++){   
                System.out.print("[" + d[i][j] + "]");
            }
            System.out.println("");
        }
        System.out.println("fine matrice");
    }
}
